package br.usp.icmc.lasdpc.gem.comm.TCP;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

import br.usp.icmc.lasdpc.gem.com.CommunicationPI;
import br.usp.icmc.lasdpc.gem.com.Message;

/**
 * It is a handler for passing messages using TCP protocol.
 * <p>
 * Basically, it implements CommunicationPI using TCP
 * 
 * @author Lourenco Alves
 * 
 * @see br.usp.icmc.lasdpc.gem.comm.CommunicationPI
 */
public class TCPHandler implements CommunicationPI {
	
	/** Default value for the port to be opened */
	public static final int DEFAULT_PORT = 9756;
	/** Object that represents the socket */
	private ServerSocket socket;
	/** Port in which the socket was opened */
	private int port;
	
	/**
	 * Create an object to bind in the local host based in the port.
	 * @param port number of the port to be bound
	 */
	public TCPHandler(int port) {
		this.port = port;
	}
	
	/**
	 * Create an object to bind in the local host based in the DEFAULT_PORT 
	 * 
	 * @see br.usp.icmc.lasdpc.gem.comm.TCP.TCPHandler#DEFAULT_PORT
	 */
	public TCPHandler() {
		this(DEFAULT_PORT);
	}

	/**
	 * Implements the method that receive a message from the opened socket.
	 * <p>
	 * Case the socket is closed, it returns null.
	 */
	@Override
	public Message receive() {
		if (socket.isClosed()) {
			System.err.println("Warning: trying to receive a message from a close socket.");
			return null;
		}
		
		Message result = null;
		
		try {
			Socket client = socket.accept();
			ObjectInputStream ois = new ObjectInputStream(client.getInputStream());
			
			result = (Message) ois.readObject();
			
			//ois.close();
			//client.close();
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}
		
		return result;
	}

	/**
	 * Implements the method to send a message through the network.
	 */
	@Override
	public void send(Message message) {
		if (socket.isClosed()) {
			System.err.println("Warning: trying to send a message using a closed socket.");
			return;
		}
		
		Socket destination = null;
		
		try {
			InetAddress addr = message.getDestination().getAddress();
			int port = message.getDestination().getPort();
			
			destination = new Socket(addr, port);
			ObjectOutputStream oos = new ObjectOutputStream(destination.getOutputStream());
			
			oos.writeObject(message);
			
			//oos.close();
			//destination.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Create a TCP server socket.
	 */
	@Override
	public void start() throws IOException {
		socket = new ServerSocket(port);
	}

	/**
	 * Stop the TCP server socket to listen to.
	 */
	@Override
	public void stop() {
		try {
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Get method for port
	 * @return
	 */
	public int getPort() {
		return port;
	}
	
	/**
	 * Set method for port
	 * @param port
	 */
	public void setPort(int port) {
		this.port = port;
	}
	
	/**
	 * Get method for socket
	 * @return
	 */
	public ServerSocket getSocket() {
		return socket;
	}
	
}
